#coding:utf-8
import random

import pygame # 安装 pip install pygame
from itertools import cycle # 导入迭代工具
from pygame.locals import * # 导入pygame中的常量
import sys
# 设置常量
SCREENWIDTH=822 # 像素
SCREENHEIGHT=199
FPS=30 # 30表示的是30毫秒 更新画面的时间

# 游戏结束的函数
def game_over():
    bump_audio=pygame.mixer.Sound('./audio/bump.wav')
    bump_audio.play() #
    # 加载游戏结束的图片
    over_img=pygame.image.load('./images/gameover.gif').convert_alpha()
    #获取窗体的宽和高
    screen_w=pygame.display.Info().current_w
    screen_h=pygame.display.Info().current_h
    # 绘制图片
    SCREEN.blit(over_img,((screen_w-over_img.get_width())/2,
                          (screen_h-over_img.get_height())/2))


# 游戏的主入口
def main_game():
    score=0 # 得分
    over=False # 标记游戏是否结束False表示未结束
    global  SCREEN ,FPSCLOCK  # 声明这两个变量是全局变量（常量）
    #初始化pygmae
    pygame.init()
    # 使用时钟控制循环的执行，
    FPSCLOCK=pygame.time.Clock() # 时钟
    # 创建一个窗体
    SCREEN=pygame.display.set_mode((SCREENWIDTH,SCREENHEIGHT))
    # 设置窗体的标题
    pygame.display.set_caption('玛丽冒险')
    # 创建两个地图类的对象
    bg1=MyMap(0,0)
    bg2=MyMap(800,0) #
    # 创建玛丽对象
    marie=Marie()
    # 添加障碍物的时间
    add_obstacle_time=0
    #障碍物列表
    lst=[]
    # 创建背景音乐按钮对象
    music_button=Music_Button()
    btn_img=music_button.open_img # 设置背景音乐按钮的默认图片
    music_button.bg_music.play(-1) # 循环播放背景音乐

    while True:
        # 获取事件
        for event in pygame.event.get():
            # 如果单击了关闭窗体，窗体就关闭
            if event.type==QUIT:
                pygame.quit() # 退出窗口
                sys.exit()
            # 按键按下， 并且是空格键
            if event.type==KEYDOWN and event.key==K_SPACE:
                if marie.rect.y>=marie.lowest_y: # 如果玛丽在地面上
                    marie.jump_audio.play() # 播放跳跃的声音
                    marie.jump()
                if over==True:
                    main_game() # 重新开始

            if event.type==pygame.MOUSEBUTTONUP:#判断鼠标事件
                if over==False: # 游戏没有结束
                    if music_button.is_select():#鼠标是否落在按钮的范围内
                        if music_button.is_open:  #如果背景音乐正在播放，现在需要停止
                            btn_img=music_button.close_img
                            music_button.is_open=False
                            music_button.bg_music.stop()
                        else:
                            btn_img=music_button.open_img
                            music_button.is_open=True
                            music_button.bg_music.play(-1)


        # 编写地图的代码
        if over==False:

            bg1.map_update()
            bg1.map_rolling()

            bg2.map_update()
            bg2.map_rolling()
            # 玛丽对象开始移动
            marie.move()
            marie.draw_marie()

            #计算障碍物间隔时间
            if add_obstacle_time>=1300: # 1300也是自己写
                r=random.randint(0,100)
                if r>40:
                    # 创建障碍物对象
                    obstacle=Obstacle()
                    lst.append(obstacle)
                #重置添加障碍物的时间
                add_obstacle_time=0
            #遍历列表

            for i in range(len(lst)):
                #障碍物移动
                lst[i].obstacle_move()
                #绘制障碍物
                lst[i].draw_obstacle()
                #玛丽是否碰撞到障碍物
                if pygame.sprite.collide_rect(marie,lst[i]):
                    over=True  #表示游戏结束了
                    game_over()# 调用game_over()函数
                    music_button.bg_music.stop() # 停止背景音乐
                else:
                    if lst[i].rect.x+lst[i].rect.width<marie.rect.x:
                        score+=lst[i].get_score()
                #显示分数
                lst[i].show_score(score)

        # 增加障碍物的时间
        add_obstacle_time+=20
        #绘制背景音乐图片
        SCREEN.blit(btn_img,(20,20))
        # 更新整个窗体
        pygame.display.update()
        # 循环每多长时间执行一次
        FPSCLOCK.tick(FPS)

# 定义一个地图类
class MyMap():
    def __init__(self,x,y):
        # 加载背景图片
        self.bg=pygame.image.load('images/bg.png').convert_alpha()
        self.x=x
        self.y=y

    # 地图在不断的滚动

    def map_rolling(self):
        if self.x<-800: # 判断地图是否移出屏幕
            self.x=800
        else:
            self.x-=5 #
    # 更新地图
    def map_update(self):
        # 重新绘制在屏幕上的位置
        SCREEN.blit(self.bg,(self.x,self.y))

#玛丽类
class Marie:
    def __init__(self):
        # 初始化玛丽矩形 Rect(left, top, width, height)
        self.rect=pygame.Rect(0,0,0,0)
        self.jumpState=False # 跳跃的状态
        self.jumpHeight=100 # 跳跃的高度
        self.lowest_y=110 # 最低坐标（在地面上）
        self.jumpValue=0 # 跳跃的增变量
        #玛丽动图的索引
        self.marieIndex=0
        self.marieIndexGen=cycle([0,1,2])
        # 图片在哪呢？
        self.adventure_img=(
            pygame.image.load('./images/adventure1.png').convert_alpha(),
            pygame.image.load('./images/adventure2.png').convert_alpha(),
            pygame.image.load('./images/adventure3.png').convert_alpha()

        )
        self.jump_audio=pygame.mixer.Sound('./audio/jump.mp3')
        # 绘制矩形 ，矩形的大小与第一张图版的大小相同
        self.rect.size=self.adventure_img[0].get_size() # 图片大小
        self.x=50 # 绘制玛丽的x坐标
        self.y=self.lowest_y
        self.rect.topleft=(self.x,self.y) # 确定了图片在窗体中的位置

    #跳跃的开关
    def jump(self):
        self.jumpState=True
    #小玛丽只在y轴上移动
    def move(self):
        if self.jumpState: # self.jumpState的值为True
            if self.rect.y>=self.lowest_y:  #说明现在是站在地面上
                self.jumpValue=-5 #以5像素向上移动
            if self.rect.y<self.lowest_y-self.jumpHeight: # 到达了顶部
                self.jumpValue=5
            self.rect.y+=self.jumpValue
            # 关闭跳跃开关
            if self.rect.y>=self.lowest_y: # 说明回到地面了
                self.jumpState=False

    # 绘制玛丽
    def draw_marie(self):
        marieIndex=next(self.marieIndexGen) # Python内置的next方法
        #绘制
        SCREEN.blit(self.adventure_img[marieIndex],(self.x,self.rect.y))
# 障碍物类
class Obstacle:
    score=1 # 分数
    move=10 # 移动距离
    obstacle_y=170  # 障碍物的y坐标
    def __init__(self):
        # 初始化障碍物矩形Rect(left, top, width, height)
        self.rect=pygame.Rect(0,0,0,0) # 创建了一个对象，这个矩形的大小最终会由图形的大小决定
        # 加载障碍物的图片
        self.pipe=pygame.image.load('./images/pipe.png').convert_alpha()
        self.missile=pygame.image.load('./images/missile.png').convert_alpha()
        #加载分数的图片
        self.numbers=(
            pygame.image.load('./images/0.gif').convert_alpha(),
            pygame.image.load('./images/1.gif').convert_alpha(),
            pygame.image.load('./images/2.gif').convert_alpha(),
            pygame.image.load('./images/3.gif').convert_alpha(),
            pygame.image.load('./images/4.gif').convert_alpha(),
            pygame.image.load('./images/5.gif').convert_alpha(),
            pygame.image.load('./images/6.gif').convert_alpha(),
            pygame.image.load('./images/7.gif').convert_alpha(),
            pygame.image.load('./images/8.gif').convert_alpha(),
            pygame.image.load('./images/9.gif').convert_alpha(),
        )
        #加载加分时的音效
        self.score_audio=pygame.mixer.Sound('./audio/score.wav')
        #使用0和1表示两个障碍物 ，0表示火箭，1表示管道
        r=random.randint(0,1) # [0,1]包含0也包含1
        if r==0:
            self.image=self.missile #显示火箭图片
            self.move=15 # 火箭的移动速度
            self.obstacle_y=90
        else:
            self.image=self.pipe
        #将图片显示在矩形里
        self.rect.size=self.image.get_size()
        self.width,self.height=self.rect.size # 系列解包赋值
        #为障碍物绘制坐标
        self.x=800
        self.y=self.obstacle_y
        self.rect.center=(self.x,self.y)

    def obstacle_move(self):
        self.rect.x-=self.move
    # 绘制障碍物
    def draw_obstacle(self):
        SCREEN.blit(self.image,(self.rect.x,self.rect.y))

    def get_score(self):
        tmp=self.score
        if tmp==1:
            self.score_audio.play() # 播放加分的音乐
        self.score=0
        return tmp

    def show_score(self,score):
        self.score_digits=[int(i) for i in list(str(score))]
        # 分数的横向坐标显示的位置
        Xoffset=700
        for digit in self.score_digits:
            # 绘制分数
            SCREEN.blit(self.numbers[digit],(Xoffset,SCREENHEIGHT*0.1))
            Xoffset+=self.numbers[digit].get_width()
# 背景音乐按钮
class Music_Button:
    is_open=True # 默认是播放背景音乐的

    def __init__(self):
        self.open_img=pygame.image.load('./images/btn_open.png').convert_alpha()
        self.close_img=pygame.image.load('./images/btn_close.png').convert_alpha()
        self.bg_music=pygame.mixer.Sound('./audio/bg_music.mp3')
    # 判断鼠标是否点在了按钮的范围内
    def is_select(self):
        #获取鼠标的坐标
        point_x,point_y=pygame.mouse.get_pos()
        w,h=self.open_img.get_size() # 系列解包赋值
        #判断鼠标是否落到了按钮的范围内
        in_x=point_x>20 and point_x<20+w
        in_y=point_y>20 and point_y<20+h
        return in_x,in_y
if __name__ == '__main__': # 以主程序的方式运行程序
    main_game() # 以下代码的执行只能是单击键时被执行